package ar.com.angrymachine.hunter.utils;

import ar.com.angrymachine.hunter.Activity;
import ar.com.angrymachine.hunter.ActivitySet;
import ar.com.angrymachine.hunter.Locale;
import ar.com.angrymachine.hunter.activities.ChestActivity;
import ar.com.angrymachine.hunter.activities.CollapsingExploreActivity;
import ar.com.angrymachine.hunter.activities.ExploreActivity;
import ar.com.angrymachine.hunter.activities.FightActivity;
import ar.com.angrymachine.hunter.activities.MeltingChestActivity;
import ar.com.angrymachine.hunter.activities.SpawnerActivity;
import ar.com.angrymachine.hunter.activities.TrapActivity;
import ar.com.angrymachine.hunter.activities.TrapDoorActivity;
import ar.com.angrymachine.hunter.gameplay.Chest;
import ar.com.angrymachine.hunter.gameplay.Database;
import ar.com.angrymachine.hunter.gameplay.InventoryObject;
import ar.com.angrymachine.hunter.gameplay.Monster;
import ar.com.angrymachine.hunter.gameplay.Room;
import ar.com.angrymachine.hunter.gameplay.Trap;
import ar.com.angrymachine.hunter.gameplay.Treasure;
import ar.com.angrymachine.utils.ProbabilityDistributor;
import ar.com.angrymachine.utils.Util;

public class DungeonGenerationUtils
{
    private static final int EVENT_ENCOUNTER = 0;
    private static final int EVENT_CHEST = 1;
    private static final int EVENT_TRAP = 2;
    private static final int EVENT_COLLAPSING = 3;
    private static final int EVENT_ALTAR = 4;
    
    private static final int[][] EVENTS = 
    {
    	{EVENT_ENCOUNTER,	13},
    	{EVENT_CHEST,		 4},
    	{EVENT_TRAP,		 4},
    	{EVENT_COLLAPSING,	 6},
    	{EVENT_ALTAR,		 2}
    };
    
    public static ExploreActivity populateLocation(ActivitySet dungeon, ExploreActivity location, int depth, int events)
    {
        // distribute events through time
        int[] times = createEventTimeline(events, location.maxExploration);
        // distribute probability of the type of events
        ProbabilityDistributor pd = createProbabilityDistributorForEvents();
        // assign a particular event to the current location
        for (int i = 0; i < times.length; i++)
        {
            int event = pd.getRandomEvent();
            insertEvents(dungeon, location, times[i], event, depth);
        }
        return location;
    }
    
    private static void insertEvents(ActivitySet dungeon, ExploreActivity location, int time, int event, int depth)
    {
    	int eventLevel = depth+1;
        switch(event)
        {
            case EVENT_ENCOUNTER:
            {
            	insertEncounter(location, time, eventLevel, null);
                break;
            }
            case EVENT_CHEST:
            {
                Chest t = createChest(eventLevel);
            	int r = Util.getRandom(0,2);
            	if(r == 1)
            	{
            		location.addDiscovery(new ChestActivity(t), time);
            	}
            	else
            	{
            		location.addDiscovery(new MeltingChestActivity(t, 1500), time);
            	}
            	break;
            }
            case EVENT_TRAP:
            {
                Trap t = createTrap(eventLevel);
                location.addDiscovery(new TrapActivity(t), time);
                break;
            }
            case EVENT_COLLAPSING:
            {
            	CollapsingExploreActivity collapsing = new CollapsingExploreActivity((Room)Database.getRandomLeveledEntity(-1, Database.rooms), 2000, 2100 );
            	Chest c = createChest(2);
            	collapsing.addDiscovery(new ChestActivity(c),3000);
            	location.addDiscovery(collapsing, time);
            	break;
            }
            case EVENT_ALTAR:
            {
            	Activity a = new SpawnerActivity(eventLevel, eventLevel*1000*Util.getRandom(3,10));
            	// spawners must be updated all the time, even when they are not visible
            	dungeon.globalActivities.add(a);
            	location.addDiscovery(a, time);
            	break;
            }
            default:
            	// something is really broken
            	throw new RuntimeException("failed to create the event: "+event);
        }
    }
    
    private static Trap createTrap(int level)
    {
        Trap newTrap = (Trap) Database.getRandomLeveledEntity(level, Database.traps);
        newTrap = (Trap) newTrap.clone();
        newTrap.treasure = createTreasure(level, null);
        return newTrap;
    }

    static int[] createEventTimeline(int events, int time)
    {
        int[] eventTimes = new int[events];
        // the last event goes last
        eventTimes[events-1] = time;
        events--;
        // the rest goes distributed in time
        int lastTime = 0;
        for (int i = 0; i < events; i++)
        {
        	eventTimes[i] = lastTime + time/eventTimes.length;
            lastTime = eventTimes[i]; 
        }
        return eventTimes;
    }
    
    private static ProbabilityDistributor createProbabilityDistributorForEvents()
    {
    	int[] probabilities = new int[EVENTS.length];
    	for (int i = 0; i < probabilities.length; i++)
		{
			probabilities[i] = EVENTS[i][1];
		}
    	return new ProbabilityDistributor(probabilities);
    }
    
    // door probability goes between 0 and 100
    public static Activity lockBehindDoor(ActivitySet dungeon, ExploreActivity room, int doorProbability)
    {
        // should this location be secured behind a door?
        if(Util.getRandom(0,100) <= doorProbability)
        {
        	return new TrapDoorActivity(room);
        }
        else
        {
        	return room;
        }
    }
    
    public static ExploreActivity createRoom(ActivitySet dungeon, int level)
    {
    	Room room = (Room) Database.getRandomLeveledEntity(level, Database.rooms);
        // set out the exploring time for the node
        int time = level * 800 + 1000;
        // create the activities
        ExploreActivity node = new ExploreActivity(room, time);
        DungeonGenerationUtils.populateLocation(dungeon, node, level, Util.getRandom(1,3));
    	return node;
    }
    
    private static void insertEncounter(ExploreActivity location, int time, int level, Treasure specialTreasure)
    {
    	// there are 2 types of monsters in an encounter:
    	// lead monster and minions
    	// create the lead
    	Monster lead = createMonster(level, specialTreasure);
    	// create an amount of minions
    	int minionCount = Util.getRandom(0,2);
    	for (int i = 0; i < minionCount; i++)
		{
    		location.addDiscovery(new FightActivity(createMonster(level-1, null)), time);
		}
    	location.addDiscovery(new FightActivity(lead), time);
    }
    
    public static Monster createMonster(int level, Treasure specialTreasure)
    {
        Monster newMonster = (Monster) Database.getRandomLeveledEntity(level, Database.enemies);
        newMonster = (Monster) newMonster.clone();
        // assign treasure
        if(specialTreasure != null)
        {
            newMonster.treasure = specialTreasure;
        }
        else
        {
            newMonster.treasure = createTreasure(level, null);
        }
        return newMonster;
    }
    
    private static Chest createChest(int level)
    {
        Chest newChest = (Chest) Database.getRandomLeveledEntity(level, Database.chests);
        newChest = (Chest) newChest.clone();
        newChest.treasure = createTreasure(level, null);
        return newChest;
    }
    
    private static Treasure createTreasure(int level, InventoryObject object)
    {
        // description
        String description = object != null ? object.name : Locale.GOLD;
        return new Treasure(level * 100, object, level * 500, description);
    }

}
